package org.lemma.number;

import static org.lemma.number.Natural.ZERO;
import static org.lemma.number.Natural.ONE;
import org.lemma.number.Numeric.Sign;
import java.io.Serializable;
import javax.annotation.Author;
import javax.annotation.Copyright;
import javax.annotation.Version;

/**
 * <p>
 * An <dfn>Integer</dfn> is a representation of a whole quantity (e.g. an element of
 * the set {0, 1, ..., n}) or its negative (e.g. an element of the set {-1, ..., -n}).
 * </p>
 * <div class="references">
 *  <cite>
 *  "Integer." <i>Wikipedia, The Free Encyclopedia</i>.
 *  1 Jan 2008, 20:57 UTC. Wikimedia Foundation, Inc.  4 Jan 2008
 *  <a href="http://en.wikipedia.org/w/index.php?title=Integer&amp;oldid=181444157">
 *  http://en.wikipedia.org/w/index.php?title=Integer&amp;oldid=181444157</a>
 *  </cite>
 * </div>
 *
 * @author Chris Beatty [christopher.beatty@gmail.com]
 * @version 1.0.0
 */
@Version (
    major="1", minor="0", patch="0", 
    date="Jun 16, 2008 2:22:06 PM",
    authors = {
        @Author (name="Chris Beatty", email="christopher.beatty@gmail.com")
    }
) @Copyright 
public class Integer extends Rational implements Serializable {
    
    /**
     * Determines if a de-serialized file is compatible with this class.
     *
     * Maintainers must change this value if and only if the new version
     * of this class is not compatible with old versions. See Sun docs
     * for <a href="http://java.sun.com/products/jdk/1.1/docs/guide
     * /serialization/spec/version.doc.html">details.</a>
     */
    private static final long serialVersionUID = 1L;

    /**
     * Static constant for the value <dfn>-1</dfn>
     */
    public static final Integer NEGATIVE_ONE = new Integer(-1);

    /**
     * The sign for this {@code Integer}
     */
    private Sign sign = Sign.None;

    /**
     * The unsigned magnitude for this {@code Integer}
     */
    private Natural magnitude = ZERO;

    /**
     * Default class constructor
     * Creates and initializes a new instance of {@code Integer}
     */
    protected Integer() {
        super(ZERO, ONE);
    }

    /**
     * Class Constructor
     * Creates a new instance of {@code Integer} and initializes
     * its {@linkplain Sign sign} and {@linkplain Natural magnitude}
     *
     * @param s A {@link Sign}
     * @param mag A {@link Natural} number
     */
    public Integer(Sign s, Natural mag) {
        this();
        //Set the Sign for this integer
        if(s != null) {
            this.sign = s;
        } else {
            throw new IllegalArgumentException("Sign is NULL");
        }
        //Set the magnitude for this integer
        if(mag != null) {
            this.magnitude = mag;
        } else {
            throw new IllegalArgumentException("Magnitude is NULL");
        }
    }

    /**
     * Class Constructor
     * Creates a new instance of {@code Integer} and initializes
     * its {@linkplain Sign sign} and {@linkplain Natural magnitude}
     *
     * @param val A {@code java.lang.Integer}
     */
    public Integer(java.lang.Integer val) {
        this(getSign(val), getMagnitude(val));
    }

    /**
     * Class Constructor
     * Creates a new instance of {@code Integer} and initializes
     * its {@linkplain Sign sign} and {@linkplain Natural magnitude}
     *
     * @param val An {@code int} value
     */
    public Integer(int val) {
        this(new java.lang.Integer(val));
    }

    /**
     * Utility method to determine the {@link Sign} for a given
     * {@link java.lang.Integer}
     *
     * @param i A {@code java.lang.Integer}
     * @return A {@link Sign}
     */
    private static Sign getSign(java.lang.Integer i) {
        if(i != null) {
            switch(java.lang.Integer.signum(i.intValue())) {
                case  0: return Sign.None;
                case  1: return Sign.Positive;
                case -1: return Sign.Negative;
                default: throw new IllegalArgumentException("Unknown signum value");
            }
        } else {
            throw new IllegalArgumentException("Cannot parse NULL");
        }
    }

    /**
     * Utility method to determine the {@link Natural} value
     * magnitude for a given {@link java.lang.Integer}
     *
     * @param i A {@code java.lang.Integer}
     * @return A {@link Natural}
     */
    private static Natural getMagnitude(java.lang.Integer i) {
        if(i != null) {
            switch(java.lang.Integer.signum(i.intValue())) {
                case  0: return new Natural(i);
                case  1: return new Natural(i);
                case -1: return new Natural(i.intValue() * -1);
                default: throw new IllegalArgumentException("Unknown signum value");
            }
        } else {
            throw new IllegalArgumentException("Cannot parse NULL");
        }
    }

    /**
     * {@inheritDoc}
     *
     * @return {@inheritDoc}
     */
    @Override
    public Sign sign() {
        return this.sign;
    }    

    /**
     * {@inheritDoc}
     *
     * @return {@inheritDoc}
     */
    @Override 
    public Integer integerPart() {
        return this;
    }

    /**
     * {@inheritDoc}
     *
     * For an integer, the fractional part is always zero.
     *
     * @return {@inheritDoc}
     */
    @Override 
    public Integer fractionalPart() {
        return ZERO;
    }

    /**
     * {@inheritDoc}
     *
     * @return {@inheritDoc}
     */
    @Override
    public Integer numerator() {
        return this;
    }  

    /**
     * {@inheritDoc}
     *
     * For an integer, the denominator is always one.
     *
     * @return {@inheritDoc}
     */
    @Override
    public Integer denominator() {
        return ONE;
    }

    /**
     * Returns the unsigned magnitude of this integer as a {@linkplain
     * Natural natural number}.
     *
     * @return A {@linkplain Natural natural number}
     */
    public Natural magnitude() {
        return this.magnitude;
    }

    /**
     * {@inheritDoc}
     *
     * @return {@inheritDoc}
     */
    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        if(this.sign == Sign.Negative) {
            sb.append(this.sign.toString());
        }
        sb.append(this.magnitude.toString());
        return sb.toString();
    }

}
